package com.casa.POI.dianping;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;

import org.apache.commons.codec.binary.Base64;

public class SmartConnection implements Time {
	
	private static final String ROUTER_CREDENTIAL = "admin:13482799302";
	private static final int READ_TIMEOUT = 20000;
	private static final int CONNECT_TIMEOUT = 10000;
	private static SmartConnection smartConnection;
	private static final Object lock = new Object();
	private static final Object inputStreamLock = new Object();
	private static final  String rootUrl = "http://www.dianping.com";
	
	public static SmartConnection getInstance() {
		if (smartConnection == null) {
			synchronized (lock) {
				if (smartConnection == null) {
					smartConnection = new SmartConnection();
				}
			}
		}
		return smartConnection;
	}

	private long startTime;
	private boolean isBlocked;
	
	protected SmartConnection() {
	
	}

	public String getRootUrl() {
		return rootUrl;
	}
	
	public String getFullUrl(String urlString) {
		if (urlString.startsWith("/")) {
			return this.getRootUrl() + urlString; 
		} else {
			return urlString;
		}
	}
	
	private InputStream getInputStreamCore(String urlString) throws IOException {
		URL url = new URL(getFullUrl(urlString));
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:11.0) Gecko/20100101 Firefox/11.0");
		setConnectionParams(connection);
		return connection.getInputStream();
	}

	private void setConnectionParams(HttpURLConnection connection) {
		connection.setConnectTimeout(CONNECT_TIMEOUT);
		connection.setReadTimeout(READ_TIMEOUT);
	}
	
	public InputStream getInputStream(String urlString) throws IOException {
		synchronized (inputStreamLock) {
			if (isBlocked()) {
				try {
					inputStreamLock.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		try {
			InputStream is = getInputStreamCore(urlString);
			synchronized (inputStreamLock) {
				setBlocked(false);
				inputStreamLock.notifyAll();
			}
			return is;
		} catch (UnknownHostException e) {
			if (ConfigurationManager.getInstance().getRedial()) {
				renewIP();
				return getInputStream(urlString);
			} else {
				throw e;
			}
		} catch (IOException e) {
			if (!isBlockedException(e))
				throw e;
			setBlocked(true);
			synchronized (inputStreamLock) {
				if (!isBlocked()) {
					return getInputStream(urlString);
				} else {
					Console.newLineWriteLine("blocked by dianping!");
					resetStartTime();
					while (true) {
						try {
							handleBlock();
							InputStream is = getInputStreamCore(urlString);
							setBlocked(false);
							inputStreamLock.notifyAll();
							return is;
						} catch (IOException e1) {
							if (!isBlockedException(e1))
								throw e;
						}
					}
				}
			}
		}
	}

	private void handleBlock() {
		if (!ConfigurationManager.getInstance().getRedial()) {
			waitToRetry();
		} else {
			renewIP();
		}
	}

	private void renewIP() {
		try {
			Console.writeLine("renew IP address...");
			URL url = new URL("http://192.168.1.1/userRpm/StatusRpm.htm?Disconnect=dummy&wan=1");
			HttpURLConnection connection = (HttpURLConnection) url.openConnection();
			String credential = ROUTER_CREDENTIAL;
			connection.addRequestProperty("Authorization", "Basic " + Base64.encodeBase64String(credential.getBytes("UTF-8")));
			this.setConnectionParams(connection);
			InputStream is = connection.getInputStream();
			is.close();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void waitToRetry() {
		try {
			int total = ConfigurationManager.getInstance().getBatchIntervalSeconds();
			for (int i = 0; i < total; i++) {
				Console.rewrite(TimeUtils.getTimedText(TimeUtils.getTimeString(getElapsedTime())));
				Thread.sleep(1000);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	private boolean isBlockedException(IOException e) {
		return !(e instanceof FileNotFoundException) && e.getMessage().contains("403");
	}

	@Override
	public long getStartTime() {
		return this.startTime;
	}
	
	public void resetStartTime() {
		this.startTime = System.currentTimeMillis() / 1000;
	}

	@Override
	public long getElapsedTime() {
		return TimeUtils.getElapsedTime(this.getStartTime());
	}

	public boolean isBlocked() {
		return isBlocked;
	}

	public void setBlocked(boolean isBlocked) {
		this.isBlocked = isBlocked;
	}
	
}
